/*
    Questa classe contiene tutti gli oggetti ed i metodi del simulatore
    Una eventuale console, o direttamente l'interfaccia grafica,
    interagisce esclusivamente con questa classe
 */


using namespace std;

class Simulator {
private:
    ASs vettoreAS;
    PhysicalNet reteFisica;
    LogicalNet reteLogica;
    TrafficNet traffico;
	SimulateAttack attacco;
	
public:
    Simulator();
    Simulator(int n);
    bool physicalConfig(); // legge il file di configurazione ed istanzia AS e link fisici
    bool trafficConfig(); // legge il file di configurazione ed istanzia il traffico
    bool saveConfig(string filename); // salva la configurazione corrente (AS, link fisici e traffico) nel file di configurazione
    bool addAS(int number, string name, string owner, bool state);
    bool addLink(int i, int j, string policy, int bandwidth, int latency, bool state);
    bool calculateLogicalNet();
    bool setTraffic(int i, int j, string key, int value, bool state);
    bool setTraffic(int i, int j, int value1, int value2, int value3, bool state);
    bool addTraffic(int i, int j, string key, int value);
    bool removeTraffic(int i, int j, string key, int value);
    bool deleteTraffic(int i, int j); //tutto il traffico da i a j
    bool deleteTraffic(int i, int j, string key); //solo il traffico in un tipo
    int getBandwidthUsageLink(int i, int j);
    bool checkLink(int i, int j);
    bool checkBandwidth();
    bool killAS(int number);
	time_t startDOSAttack( int ASOrigin, int ASVictim, int amount );
    bool update();

    //funzioni di stampa temporanee, dopo si laveno
    void printASs();
    void printPhysicalNet();
    void printLogicalNet();
    void printASPath(int i, int j);
    void printASPaths();
    void printTraffics();
};

Simulator::Simulator() {
    ;
}

Simulator::Simulator(int n) {
    vettoreAS.resize(n);
    vettoreAS.init();
    reteFisica.resize(n);
    reteFisica.init();
    reteLogica.resize(n);
    reteLogica.init();
    traffico.resize(n);
    traffico.init();
}

bool Simulator::physicalConfig() {
    string file = "config.xml";
    TiXmlDocument doc(file.c_str());

    // Parsing
    if (doc.LoadFile()) {
        TiXmlElement *root = doc.RootElement();

        // loops over the config file
        for (TiXmlElement* item = root->FirstChildElement(); item; item = item->NextSiblingElement()) {
            if (item->ValueStr().compare("AS") == 0) { // Add AS
                vettoreAS.addAS(atoi(item->Attribute("number")), item->Attribute("name"), item->Attribute("owner"), item->Attribute("state"));
            }
            else if (item->ValueStr().compare("PhysicalLink") == 0) { // Add physical link
                reteFisica.addLink(atoi(item->Attribute("ASOrigin")), atoi(item->Attribute("ASDestination")), item->Attribute("policy"), atoi(item->Attribute("bandwith")), atoi(item->Attribute("latency")), item->Attribute("state"));
            }
        }
    }
}

bool Simulator::trafficConfig() {
    string file = "config.xml";
    TiXmlDocument doc(file.c_str());

    // Parsing
    if (doc.LoadFile()) {
        TiXmlElement *root = doc.RootElement();

        // loops over the config file
        for (TiXmlElement* item = root->FirstChildElement(); item; item = item->NextSiblingElement()) {
            if (item->ValueStr().compare("Traffic") == 0) { // Add AS
                setTraffic(atoi(item->Attribute("ASOrigin")), atoi(item->Attribute("ASDestination")), "web", atoi(item->Attribute("web")), item->Attribute("state"));
                setTraffic(atoi(item->Attribute("ASOrigin")), atoi(item->Attribute("ASDestination")), "voip", atoi(item->Attribute("voip")), item->Attribute("state"));
                setTraffic(atoi(item->Attribute("ASOrigin")), atoi(item->Attribute("ASDestination")), "p2p", atoi(item->Attribute("p2p")), item->Attribute("state"));
            }
        }
    }
}

bool Simulator::saveConfig(string filename) {
    TiXmlDocument doc;
    TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "UTF-8", "");

    doc.LinkEndChild(decl); // Inserisci la dichiarazione

    // Inserisci la radice
    TiXmlElement *root = new TiXmlElement("Net");
    doc.LinkEndChild(root);

    // Inserisci tutti gli AS
    for (int i = 1; i < vettoreAS.getSize(); i++) {
        TiXmlElement *AS = new TiXmlElement("AS");
        AS->SetAttribute("number", i);
        AS->SetAttribute("name", vettoreAS.getName(i));
        AS->SetAttribute("owner", vettoreAS.getOwner(i));
        AS->SetAttribute("state", vettoreAS.getState(i));

        root->LinkEndChild(AS);
    }

    // Inserisci tutti i link fisici
    for (int i = 1; i < reteFisica.getSize(); i++) {
        for (int j = 1; j < reteFisica.getSize(); j++) {
            if ((reteFisica.getPolicy(i, j)).empty())
                continue;

            TiXmlElement *link = new TiXmlElement("PhysicalLink");
            link->SetAttribute("ASOrigin", i);
            link->SetAttribute("ASDestination", j);
            link->SetAttribute("policy", reteFisica.getPolicy(i, j));
            link->SetAttribute("bandwith", reteFisica.getBandwidth(i, j));
            link->SetAttribute("latency", reteFisica.getLatency(i, j));
            link->SetAttribute("state", reteFisica.getState(i, j));

            root->LinkEndChild(link);
        }
    }

    // Inserisci il traffico presente nella rete
    for (int i = 1; i < traffico.getSize(); i++) {
        for (int j = 1; j < traffico.getSize(); j++) {
            if(traffico.getState(i, j)==0)
                continue;

            TiXmlElement *traffic = new TiXmlElement("Traffic");
            traffic->SetAttribute("ASOrigin", i);
            traffic->SetAttribute("ASDestination", j);
            traffic->SetAttribute("web", traffico.getTraffic(i, j, "web"));
            traffic->SetAttribute("voip", traffico.getTraffic(i, j, "voip"));
            traffic->SetAttribute("p2p", traffico.getTraffic(i, j, "p2p"));
            traffic->SetAttribute("state", traffico.getState(i, j));

            root->LinkEndChild(traffic);
        }
    }

    doc.SaveFile(filename);
}

bool Simulator::addAS(int i, string name, string owner, bool state) {
    vettoreAS.addAS(i, name, owner, state);
}

bool Simulator::addLink(int i, int j, string policy, int bandwidth, int latency, bool state) {
    reteFisica.addLink(i, j, policy, bandwidth, latency, state);
}

bool Simulator::calculateLogicalNet() {
    reteLogica.calculateASPaths(&reteFisica);
    reteLogica.updateNet(&reteFisica);
}

bool Simulator::setTraffic(int i, int j, string key, int value, bool state) {
    traffico.setTraffic(i, j, key, value, state);
    list < pair<int, int> > lista = reteLogica.getASPathPair(i, j);
    list < pair<int, int> >::iterator it;
    for (it = lista.begin(); it != lista.end(); it++) {
        //cout << "consumo banda " << value << " dal link fisico (" <<(*it).first<<"-"<<(*it).second<<") per il traffico "<<i<<"-"<<j<<endl;
        reteFisica.addBandwidthUsage((*it).first, (*it).second, value);
    }
}

bool Simulator::setTraffic(int i, int j, int value1, int value2, int value3, bool state) {
    setTraffic(i, j, "web", value1, state);
    setTraffic(i, j, "voip", value2, state);
    setTraffic(i, j, "p2p", value3, state);
}

bool Simulator::addTraffic(int i, int j, string key, int value) {
    traffico.addTraffic(i, j, key, value);
}

bool Simulator::removeTraffic(int i, int j, string key, int value) {
    traffico.removeTraffic(i, j, key, value);
}

bool Simulator::deleteTraffic(int i, int j) {
    traffico.setTraffic(i, j, "web", 0, false);
    traffico.setTraffic(i, j, "voip", 0, false);
    traffico.setTraffic(i, j, "p2p", 0, false);
}

bool Simulator::deleteTraffic(int i, int j, string key) {
    traffico.setTraffic(i, j, key, 0, true); //da correggere il true, non dovrebbe passare nulla
}

int Simulator::getBandwidthUsageLink(int i, int j) {
    int somma = 0;
    list < pair<int, int> > lista = reteLogica.getASPathsThroughLink(i, j);
    list < pair<int, int> >::iterator it;
    for (it = lista.begin(); it != lista.end(); it++) {
        somma = somma + traffico.getTraffic((*it).first, (*it).second);
        //cout << "aggiunta banda "<<traffico.getTraffic(origin,destination)<<" da "<<origin<<"-"<<destination<<endl;
    }
    return somma;
}

bool Simulator::checkLink(int i, int j) {
    int bandwidthCapacity = reteFisica.getBandwidth(i, j);
    int bandwidthActive = getBandwidthUsageLink(i, j);
    cout << "link " << i << "-" << j << ": capacità: " << bandwidthCapacity << "\t, passante: " << bandwidthActive << "\t, parziale: " << reteFisica.getBandwidthUsage(i,j) + reteFisica.getBandwidthUsage(j,i) << "\t => ";
    if (getBandwidthUsageLink(i, j) <= bandwidthCapacity) {
        cout << "OK" << endl;
        return true;
    } else {
        cout << "Banda insufficiente" << endl;
        return false;
    }
}

bool Simulator::checkBandwidth() {
    cout << "check della banda: " << endl;
    int n = vettoreAS.getSize();
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++)
            if (reteFisica.getState(i, j) && i < j) // i<j per controllare ogni link una sola volta
                checkLink(i, j);
    cout << endl;
}

bool Simulator::killAS(int number) {
    vettoreAS.setState(number, false);
    reteFisica.killLinksWithAS(number);
    list< pair<int,int> > ASPathsThroughAS = reteLogica.getASPathsThroughAS(number);
    //traffico.killLinksWithAS(number);
    //traffico.killLinks(ASPathsThroughAS);
}

time_t Simulator::startDOSAttack( int ASOrigin, int ASVictim, int amount ) {
	time_t timeOfAttack;
	timeOfAttack = attacco.StartDOSAttack(ASOrigin, ASVictim, amount, &traffico );
	update();
}



bool Simulator::update() {
    reteFisica.reset();
    reteLogica.reset();
    calculateLogicalNet();
    trafficConfig();
}

////////////////////////////////////////////////////////////////////////////////////
// FUNZIONI DI STAMPA TEMPORANEE, UTILI PER FARE TEST MA ALLA FINE SE NE ANDRANNO //
////////////////////////////////////////////////////////////////////////////////////

void Simulator::printASs() {
    cout << "list of all the Autonomous Systems: " << endl;
    vettoreAS.print();
    cout << endl;
}

void Simulator::printPhysicalNet() {
    int n = vettoreAS.getSize();
    cout << "Stampa della matrice fisica (banda/delay) :" << endl;

    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++) {
            if (i == 0 && j == 0)
                cout << endl;
            else {
                if (j == 0 && i != 0)
                    cout << vettoreAS.getName(i);

                if (i == 0 && j != 0)
                    cout << "\t" << vettoreAS.getName(j);

                if (i != 0 && j != 0) {
                    if (reteFisica.getState(i, j))
                        cout << "\t" << reteFisica.getBandwidth(i, j) << "/" << reteFisica.getLatency(i, j);
                    else
                        cout << "\t" << "x";
                }

                if (j == n - 1)
                    cout << endl;
                if (i == 0 && j == n - 1)
                    cout << endl;
            }
        }
    cout << endl;
}

void Simulator::printLogicalNet() {
    int n = vettoreAS.getSize();
    cout << "Stampa della matrice logica (banda/delay) :" << endl;

    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++) {
            if (i == 0 && j == 0)
                cout << endl;
            else {
                if (j == 0 && i != 0)
                    cout << vettoreAS.getName(i);

                if (i == 0 && j != 0)
                    cout << "\t" << vettoreAS.getName(j);

                if (i != 0 && j != 0) {
                    if (reteLogica.getState(i, j))
                        cout << "\t" << reteLogica.getBandwidth(i, j) << "/" << reteLogica.getLatency(i, j);
                    else
                        cout << "\t" << "x";
                }

                if (j == n - 1)
                    cout << endl;
                if (i == 0 && j == n - 1)
                    cout << endl;
            }
        }
    cout << endl;
}

void Simulator::printASPath(int i, int j) {
    list<int> lista = reteLogica.getASPath(i, j);
    list<int>::iterator it;
    cout << "ASPath da " << vettoreAS.getName(i) << " a " << vettoreAS.getName(j) << " :";
    for (it = lista.begin(); it != lista.end(); it++)
        cout << " " << vettoreAS.getName(*it);
    cout << endl;
}

void Simulator::printASPaths() {
    int n = vettoreAS.getSize();
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++) {
            if (reteLogica.getState(i, j)) {
                printASPath(i, j);
            }
        }
    cout << endl;
}

void Simulator::printTraffics() {
    int n = traffico.getSize();
    cout << "Stampa della matrice del traffico (web/voip/p2p) :" << endl;

    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++) {
            if (i == 0 && j == 0)
                cout << endl;
            else {
                if (j == 0 && i != 0)
                    cout << vettoreAS.getName(i);

                if (i == 0 && j != 0)
                    cout << "\t" << vettoreAS.getName(j);

                if (i != 0 && j != 0) {
                    if (traffico.getState(i, j))
                        cout << "\t" << traffico.getTraffic(i, j, "web") << "/" << traffico.getTraffic(i, j, "voip") << "/" << traffico.getTraffic(i, j, "p2p");
                    else
                        cout << "\t" << "x";
                }

                if (j == n - 1)
                    cout << endl;
                if (i == 0 && j == n - 1)
                    cout << endl;
            }
        }
    cout << endl;
}
